package com.qf.votedemo.contestant.service.impl;

import cn.hutool.json.JSONUtil;
import cn.hutool.jwt.JWT;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.qf.votedemo.contestant.entity.Contestant;
import com.qf.votedemo.contestant.entity.Contestantphone;
import com.qf.votedemo.contestant.entity.VoteLogs;
import com.qf.votedemo.contestant.mapper.ContestantMapper;
import com.qf.votedemo.contestant.service.IContestantService;
import com.qf.votedemo.contestant.service.IContestantphoneService;
import com.qf.votedemo.phone.entity.Phone;
import com.qf.votedemo.phone.service.IPhoneService;
import com.qf.votedemo.rabbitmp.VoteLogsSend;
import com.qf.votedemo.until.LocalCache;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author lx
 * @since 2023-02-24
 */
@Service
public class ContestantServiceImpl extends ServiceImpl<ContestantMapper, Contestant> implements IContestantService {

    @Autowired
    private IContestantphoneService contestantphoneService;

    @Autowired
    private IPhoneService phoneService;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private VoteLogsSend voteLogsSend;


    /**
     * 选手投票
     *
     * @param phoneNum
     * @param contestantId
     * @throws Exception
     */
    @Transactional(propagation = Propagation.SUPPORTS)
    @Override
    public void vote(String phoneNum, Integer contestantId) throws Exception {
        //查询投票的选手是否存在
        Contestant contestant = getOne(new LambdaQueryWrapper<Contestant>().eq(Contestant::getCid, contestantId));
        if (contestant == null) {
            throw new Exception("选手不存在");
        }
        //获取电话号码id
        Phone phone = phoneService.getOne(new LambdaQueryWrapper<Phone>().eq(Phone::getPhonenumber, phoneNum));
        if (phone == null) {
            throw new Exception("该号码不存在");
        }
        Integer pid = phone.getPid();

        //查询电话号码投票选手按时间倒序
        Contestantphone contestantphones = (Contestantphone) contestantphoneService.getObj(new LambdaQueryWrapper<Contestantphone>().eq(Contestantphone::getPid, pid).orderByDesc(Contestantphone::getBallotTime).last("limit 1"), o -> o);
        //判断该电话号码是否投过票
        if (contestantphones == null) {
            //该号码第一次投票
            contestantphoneService.vote(pid, contestantId);
        } else {
            //获取当前时间戳
            long nowDate = new Date().getTime();
            //获取这个电话号码最近投票的时间戳
            long dateDb = contestantphones.getBallotTime();
            //判断最近一次投票时间是否大于60秒
            if (nowDate - dateDb < 60000) {
                throw new Exception("一分钟只可以投一票");
            }
            //按pid与cid查询投票情况
            Contestantphone contestantphoneDb = contestantphoneService.getOne(new LambdaQueryWrapper<Contestantphone>().eq(Contestantphone::getPid, pid).eq(Contestantphone::getCid, contestantId));
            //该用户第一次为这个选手投票
            if (contestantphoneDb == null) {
                contestantphoneService.vote(pid, contestantId);

            } else {
                //更新选手票数
                contestantphoneService.updateVote(contestantphoneDb, pid, contestantId);
            }
        }
    }

    /**
     * 基于redis实现投票
     *
     * @param phoneNum
     * @param contestantId
     * @param token
     */
    @Override
    public void voteRedis(String phoneNum, Integer contestantId, String token) throws Exception {
        JWT jwt = JWT.of(token);
        String phone = (String) jwt.getPayload("phone");
        if (!phone.equals(phoneNum)) {
            throw new Exception("当前手机号有误！");
        }
        boolean validate = JWT.of(token).setKey("qfjava2203".getBytes(StandardCharsets.UTF_8)).validate(0);
        if (!validate) {
            throw new Exception("token已过期请重新获取");
        }
        //投票限流策略
        //获取缓存中的电话号码
        Boolean hasKey = redisTemplate.hasKey("vote:phone" + phoneNum);
        if (hasKey) {
            throw new Exception("一分钟之内只能投一次票");
        }
        //设置投票间隔
        redisTemplate.opsForValue().set("vote:phone" + phoneNum, phoneNum, 60, TimeUnit.SECONDS);

        //生成投票流水
        VoteLogs votelogs = createVote(phoneNum, contestantId);
        String contestantphoneToJson = JSONUtil.toJsonStr(votelogs);
        redisTemplate.opsForList().rightPush("vote:list", contestantphoneToJson);
        List<VoteLogs> voteLogCache = LocalCache.voteLogCache;
        voteLogCache.add(votelogs);
        //投票流水满足10条发mq
        if (voteLogCache.size() == 10) {
            ObjectMapper objectMapper = new ObjectMapper();
            String voteLogCacheToStr = objectMapper.writeValueAsString(voteLogCache);
            //将10条投票流水发给mq
            voteLogsSend.send("vote_exchange", "vote.contestphone.log", voteLogCacheToStr);
            voteLogCache.clear();
        }

        //将投票流水发给mq
        //voteLogsSend.send("vote_exchange", "vote.contestphone.log", contestantphoneToJson);
        //统计候选人的票数
        redisTemplate.opsForZSet().incrementScore("vote:zset", contestantId, 1);


    }

    @Override
    public void getContestants() {
        List<Contestant> contestants = list();
        redisTemplate.opsForList().rightPushAll("vote:contestant", contestants);
    }

    private VoteLogs createVote(String phoneNum, Integer contestantId) throws Exception {
        VoteLogs voteLogs = new VoteLogs();
        Phone phone = phoneService.selectPhone(phoneNum);
        if (phone == null) {
            throw new Exception("号码不存在");
        }
        voteLogs.setPhoneId(phone.getPid());
        voteLogs.setContestantId(contestantId);
        voteLogs.setCreatTime(new Date());
        return voteLogs;
    }
}
